package com.zhentao.service;

import com.zhentao.dto.MonitoringQueryDto;
import com.zhentao.dto.MonitoringResultDto;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 异动监测服务
 * 实现基于24个月历史数据的异常检测和预警
 */
@Service
public class AnomalyDetectionService {

    /**
     * 综合异动检测
     */
    public Map<String, Object> comprehensiveAnomalyDetection(List<Double> historicalData, List<Double> predictions, double sensitivity) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 趋势异常检测
            Map<String, Object> trendAnomalies = detectTrendAnomalies(historicalData, sensitivity);
            result.put("trendAnomalies", trendAnomalies);
            
            // 季节性异常检测
            Map<String, Object> seasonalityAnomalies = detectSeasonalityAnomalies(historicalData, sensitivity);
            result.put("seasonalityAnomalies", seasonalityAnomalies);
            
            // 统计异常检测
            Map<String, Object> statisticalAnomalies = detectStatisticalAnomalies(historicalData, sensitivity);
            result.put("statisticalAnomalies", statisticalAnomalies);
            
            // 预测异常检测
            Map<String, Object> predictionAnomalies = detectPredictionAnomalies(historicalData, predictions, sensitivity);
            result.put("predictionAnomalies", predictionAnomalies);
            
            result.put("status", "success");
            result.put("message", "异常检测完成");
            
        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", "异常检测失败: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 基于24个月历史数据的预警检测
     * 核心预警逻辑：以前24个月增幅或降幅的最高值*1.1为基准
     */
    public List<MonitoringResultDto> detectAnomalies(List<MonitoringResultDto> data, MonitoringQueryDto query) {
        if (data == null || data.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 获取预警参数
        double alertThreshold = query.get预警阈值倍数() != null ? query.get预警阈值倍数() : 1.1;
        int historyMonths = query.get历史数据月数() != null ? query.get历史数据月数() : 24;
        
        List<MonitoringResultDto> results = new ArrayList<>();
        
        for (MonitoringResultDto item : data) {
            // 计算预警状态
            boolean isAlert = calculateAlertStatus(item, alertThreshold, historyMonths);
            item.set是否预警(isAlert);
            
            if (isAlert) {
                // 设置预警类型和原因
                String alertType = determineAlertType(item);
                String alertReason = generateAlertReason(item);
                item.set预警类型(alertType);
                item.set预警原因(alertReason);
            }
            
            results.add(item);
        }
        
        return results;
    }

    /**
     * 计算预警状态
     */
    private boolean calculateAlertStatus(MonitoringResultDto item, double threshold, int historyMonths) {
        // 这里应该基于24个月历史数据计算
        // 暂时使用简化的逻辑：基于金额和变化率判断
        
        if (item.get进出口总额() == null || item.get进出口总额().compareTo(BigDecimal.ZERO) <= 0) {
            return false;
        }
        
        double amount = item.get进出口总额().doubleValue();
        Double yoyChange = item.get进出口总额同比变化();
        Double momChange = item.get进出口总额环比变化();
        
        // 如果同比或环比变化超过阈值，标记为预警
        if (yoyChange != null && Math.abs(yoyChange) > threshold) {
            return true;
        }
        
        if (momChange != null && Math.abs(momChange) > threshold) {
            return true;
        }
        
        // 如果金额特别大（超过100万），也标记为预警
        if (amount > 1000000) {
            return true;
        }
        
        return false;
    }

    /**
     * 确定预警类型
     */
    private String determineAlertType(MonitoringResultDto item) {
        Double yoyChange = item.get进出口总额同比变化();
        Double momChange = item.get进出口总额环比变化();
        
        if (yoyChange != null && Math.abs(yoyChange) > 0.5) {
            return "同比异常";
        }
        
        if (momChange != null && Math.abs(momChange) > 0.3) {
            return "环比异常";
        }
        
        return "金额异常";
    }

    /**
     * 生成预警原因
     */
    private String generateAlertReason(MonitoringResultDto item) {
        Double yoyChange = item.get进出口总额同比变化();
        Double momChange = item.get进出口总额环比变化();
        
        if (yoyChange != null && yoyChange > 0.5) {
            return "同比增幅过大";
        }
        
        if (yoyChange != null && yoyChange < -0.5) {
            return "同比降幅过大";
        }
        
        if (momChange != null && momChange > 0.3) {
            return "环比增幅过大";
        }
        
        if (momChange != null && momChange < -0.3) {
            return "环比降幅过大";
        }
        
        return "金额异常";
    }

    /**
     * 趋势异常检测
     */
    private Map<String, Object> detectTrendAnomalies(List<Double> data, double sensitivity) {
        Map<String, Object> result = new HashMap<>();
        
        if (data.size() < 2) {
            result.put("status", "insufficient_data");
            return result;
        }
        
        // 计算线性趋势
        double slope = calculateLinearSlope(data);
        double trendStrength = Math.abs(slope);
        
        // 判断趋势是否异常
        boolean isAnomaly = trendStrength > sensitivity;
        
        result.put("isAnomaly", isAnomaly);
        result.put("trendSlope", slope);
        result.put("trendStrength", trendStrength);
        result.put("threshold", sensitivity);
        
        return result;
    }

    /**
     * 季节性异常检测
     */
    private Map<String, Object> detectSeasonalityAnomalies(List<Double> data, double sensitivity) {
        Map<String, Object> result = new HashMap<>();
        
        if (data.size() < 12) {
            result.put("status", "insufficient_data");
            return result;
        }
        
        // 计算季节性模式
        List<Double> seasonality = calculateSeasonalPattern(data);
        double seasonalityStrength = calculateSeasonalityStrength(seasonality);
        
        // 判断季节性是否异常
        boolean isAnomaly = seasonalityStrength > sensitivity;
        
        result.put("isAnomaly", isAnomaly);
        result.put("seasonalityPattern", seasonality);
        result.put("seasonalityStrength", seasonalityStrength);
        result.put("threshold", sensitivity);
        
        return result;
    }

    /**
     * 统计异常检测
     */
    private Map<String, Object> detectStatisticalAnomalies(List<Double> data, double sensitivity) {
        Map<String, Object> result = new HashMap<>();
        
        if (data.isEmpty()) {
            result.put("status", "no_data");
            return result;
        }
        
        // 计算统计指标
        double mean = calculateMean(data);
        double stdDev = calculateStandardDeviation(data);
        
        // 检测异常值（超过均值±sensitivity*标准差）
        List<Double> anomalies = new ArrayList<>();
        for (Double value : data) {
            if (Math.abs(value - mean) > sensitivity * stdDev) {
                anomalies.add(value);
            }
        }
        
        result.put("mean", mean);
        result.put("stdDev", stdDev);
        result.put("anomalies", anomalies);
        result.put("anomalyCount", anomalies.size());
        result.put("threshold", sensitivity);
        
        return result;
    }

    /**
     * 预测异常检测
     */
    private Map<String, Object> detectPredictionAnomalies(List<Double> historical, List<Double> predictions, double sensitivity) {
        Map<String, Object> result = new HashMap<>();
        
        if (historical.isEmpty() || predictions.isEmpty()) {
            result.put("status", "insufficient_data");
            return result;
        }
        
        // 计算预测误差
        double mape = calculateMAPE(historical, predictions);
        double rmse = calculateRMSE(historical, predictions);
        
        // 判断预测是否异常
        boolean isAnomaly = mape > sensitivity * 0.1 || rmse > sensitivity * 1000;
        
        result.put("isAnomaly", isAnomaly);
        result.put("mape", mape);
        result.put("rmse", rmse);
        result.put("threshold", sensitivity);
        
        return result;
    }

    /**
     * 计算线性斜率
     */
    private double calculateLinearSlope(List<Double> data) {
        if (data.size() < 2) return 0.0;
        
        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        int n = data.size();
        
        for (int i = 0; i < n; i++) {
            double x = i;
            double y = data.get(i);
            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumX2 += x * x;
        }
        
        double slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
        return slope;
    }

    /**
     * 计算季节性模式
     */
    private List<Double> calculateSeasonalPattern(List<Double> data) {
        List<Double> seasonality = new ArrayList<>();
        
        // 假设12个月为一个周期
        int period = 12;
        if (data.size() < period) {
            return seasonality;
        }
        
        for (int i = 0; i < period; i++) {
            double sum = 0;
            int count = 0;
            
            for (int j = i; j < data.size(); j += period) {
                sum += data.get(j);
                count++;
            }
            
            if (count > 0) {
                seasonality.add(sum / count);
            } else {
                seasonality.add(0.0);
            }
        }
        
        return seasonality;
    }

    /**
     * 计算季节性强度
     */
    private double calculateSeasonalityStrength(List<Double> seasonality) {
        if (seasonality.isEmpty()) return 0.0;
        
        double mean = calculateMean(seasonality);
        double variance = 0.0;
        
        for (Double value : seasonality) {
            variance += Math.pow(value - mean, 2);
        }
        
        variance /= seasonality.size();
        return Math.sqrt(variance);
    }

    /**
     * 计算均值
     */
    private double calculateMean(List<Double> data) {
        if (data.isEmpty()) return 0.0;
        return data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
    }

    /**
     * 计算标准差
     */
    private double calculateStandardDeviation(List<Double> data) {
        if (data.size() < 2) return 0.0;
        
        double mean = calculateMean(data);
        double variance = data.stream()
            .mapToDouble(d -> Math.pow(d - mean, 2))
            .average()
            .orElse(0.0);
        
        return Math.sqrt(variance);
    }

    /**
     * 计算MAPE（平均绝对百分比误差）
     */
    private double calculateMAPE(List<Double> actual, List<Double> predicted) {
        if (actual.size() != predicted.size() || actual.isEmpty()) return 0.0;
        
        double sum = 0;
        int count = 0;
        
        for (int i = 0; i < actual.size(); i++) {
            if (actual.get(i) != 0) {
                sum += Math.abs((actual.get(i) - predicted.get(i)) / actual.get(i));
                count++;
            }
        }
        
        return count > 0 ? (sum / count) * 100 : 0.0;
    }

    /**
     * 计算RMSE（均方根误差）
     */
    private double calculateRMSE(List<Double> actual, List<Double> predicted) {
        if (actual.size() != predicted.size() || actual.isEmpty()) return 0.0;
        
        double sum = 0;
        
        for (int i = 0; i < actual.size(); i++) {
            double diff = actual.get(i) - predicted.get(i);
            sum += diff * diff;
        }
        
        return Math.sqrt(sum / actual.size());
    }

    /**
     * 获取Z值（用于置信区间计算）
     */
    private double getZValue(double confidenceLevel) {
        // 简化实现，实际应该使用正态分布表
        switch ((int) (confidenceLevel * 100)) {
            case 90: return 1.645;
            case 95: return 1.96;
            case 99: return 2.576;
            default: return 1.96;
        }
    }

    /**
     * 计算置信区间
     */
    private Map<String, Double> calculateConfidenceIntervals(List<Double> data, double confidenceLevel) {
        Map<String, Double> result = new HashMap<>();
        
        if (data.size() < 2) {
            result.put("lower", 0.0);
            result.put("upper", 0.0);
            return result;
        }
        
        double mean = calculateMean(data);
        double stdDev = calculateStandardDeviation(data);
        double zValue = getZValue(confidenceLevel);
        double margin = zValue * stdDev / Math.sqrt(data.size());
        
        result.put("lower", mean - margin);
        result.put("upper", mean + margin);
        result.put("mean", mean);
        result.put("margin", margin);
        
        return result;
    }
} 

 